package com.letoken.platform.app.configuration;

import com.alibaba.fastjson.JSONObject;
import groovy.lang.Tuple3;
import lombok.Data;
import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.FunctionReturnDecoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.generated.Uint112;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.methods.request.Transaction;
import org.web3j.protocol.core.methods.response.EthCall;
import org.web3j.protocol.http.HttpService;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class PancakeSwapLPQuery {
    private static final String PAIR_CONTRACT_ADDRESS = "0xeF2058BFBd3A65A87045b73d11588587F0337526";
    private static final String BSC_RPC_URL = "https://bsc-dataseed.binance.org/";

    private static Web3j web3j = Web3j.build(new HttpService(BSC_RPC_URL));

    public static void main(String[] args) {
        try {
            System.out.println(JSONObject.toJSON(getPrice()));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static Price getPrice() throws Exception {
        BigInteger totalSupply = getTotalSupply(PAIR_CONTRACT_ADDRESS);
        //System.out.println("总 LP 供应量: " + totalSupply);

        Reserves reserves = getReserves(PAIR_CONTRACT_ADDRESS);
        /*System.out.println("代币 A 储备量: " + reserves.reserve0);
        System.out.println("代币 B 储备量: " + reserves.reserve1);*/

       /* double lpValueTokenA = reserves.reserve0.doubleValue() / totalSupply.doubleValue();
        double lpValueTokenB = reserves.reserve1.doubleValue() / totalSupply.doubleValue();

        System.out.println("每个 LP 代币对应的代币 A 数量: " + lpValueTokenA);
        System.out.println("每个 LP 代币对应的代币 B 数量: " + lpValueTokenB);*/


        // 代币 A 的价格为 1 USD
        BigDecimal tokenAPrice = BigDecimal.ONE;

        // 计算代币 B 的价格
        BigDecimal tokenBPrice = new BigDecimal(reserves.reserve0).multiply(tokenAPrice).divide(new BigDecimal(reserves.reserve1), 18, BigDecimal.ROUND_HALF_UP);
       // System.out.println("代币 B 的价格: " + tokenBPrice + " USD");

        // 计算每个 LP 代币的价值
        BigDecimal totalValue = new BigDecimal(reserves.reserve0).multiply(tokenAPrice).add(new BigDecimal(reserves.reserve1).multiply(tokenBPrice));
        BigDecimal lpTokenValue = totalValue.divide(new BigDecimal(totalSupply), 18, BigDecimal.ROUND_HALF_UP);
       // System.out.println("每个 LP 代币的价值: " + lpTokenValue + " USD");
        Price price = new Price();
        price.setCap(tokenBPrice.setScale(8, RoundingMode.DOWN));
        price.setLp(lpTokenValue.setScale(8, RoundingMode.DOWN));
        return price;
    }

    private static BigInteger getTotalSupply(String pairAddress) throws Exception {
        Function function = new Function(
                "totalSupply",
                Collections.emptyList(),
                Collections.singletonList(new TypeReference<Uint256>() {}));

        String encodedFunction = FunctionEncoder.encode(function);
        Transaction transaction = Transaction.createEthCallTransaction(null, pairAddress, encodedFunction);
        EthCall response = web3j.ethCall(transaction, org.web3j.protocol.core.DefaultBlockParameterName.LATEST).send();

        List<Type> output = FunctionReturnDecoder.decode(response.getValue(), function.getOutputParameters());
        return (BigInteger) output.get(0).getValue();
    }

    private static Reserves getReserves(String pairAddress) throws Exception {
        Function function = new Function(
                "getReserves",
                Collections.emptyList(),
                Arrays.asList(new TypeReference<Uint112>() {}, new TypeReference<Uint112>() {}, new TypeReference<Uint256>() {}));

        String encodedFunction = FunctionEncoder.encode(function);
        Transaction transaction = Transaction.createEthCallTransaction(null, pairAddress, encodedFunction);
        EthCall response = web3j.ethCall(transaction, org.web3j.protocol.core.DefaultBlockParameterName.LATEST).send();

        List<Type> output = FunctionReturnDecoder.decode(response.getValue(), function.getOutputParameters());
        BigInteger reserve0 = (BigInteger) output.get(0).getValue();
        BigInteger reserve1 = (BigInteger) output.get(1).getValue();

        return new Reserves(reserve0, reserve1);
    }

    private static class Reserves {
        public BigInteger reserve0;
        public BigInteger reserve1;

        public Reserves(BigInteger reserve0, BigInteger reserve1) {
            this.reserve0 = reserve0;
            this.reserve1 = reserve1;
        }
    }

    @Data
    public static class Price {
        public BigDecimal cap;
        public BigDecimal lp;
    }
}
